package com.xiaomaoguai.fcp.hsf.autoconfigure;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @fileName: HsfClientsRegistrar.java
 * @author: WeiHui
 * @date: 2018/11/20 21:38
 * @version: v1.0.0
 * @since JDK 1.8
 */
@Slf4j
public class HsfClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

	private ResourceLoader resourceLoader;

	private ClassLoader classLoader;

	private Environment environment;

	public HsfClientsRegistrar() {
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		//构建一级扫描器
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);
		scanner.addIncludeFilter(new AnnotationTypeFilter(HsfClient.class));
		//获取要扫描的包路径
		String packageName = ClassUtils.getPackageName(metadata.getClassName());
		log.info("====>packageName:{}", packageName);
		Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(packageName);

		//待处理的bean集合
		//hsfApi bean
		Map<Class<?>, Map<String, Object>> hsfApiBeanSet = new HashMap<>(16);
		//hsfClientBean
		Map<BeanDefinition, Map<String, Object>> hsfClientBeanSet = new HashMap<>(16);
		//scan packageBean
		Map<String, Map<String, Object>> hsfClientPackageAttributes = new HashMap<>(16);
		//准备要扫描的二级，hsfClient bean
		Set<String> basePackages = new HashSet<>();

		//对HsfClient处理分组
		groupHsfClient(candidateComponents, hsfApiBeanSet, hsfClientBeanSet, hsfClientPackageAttributes, basePackages);

		//构建二级的bean
		ClassPathScanningCandidateComponentProvider hsfClientScanner = getHsfClientScanner();
		hsfClientScanner.setResourceLoader(this.resourceLoader);
		for (String basePackage : basePackages) {
			log.info("====>扫描HSF-API包：{}", basePackage);
			Set<BeanDefinition> hsfBean = hsfClientScanner.findCandidateComponents(basePackage);
			Map<String, Object> hsfAttributes = hsfClientPackageAttributes.get(basePackage);
			for (BeanDefinition beanDefinition : hsfBean) {
				String beanClassName = beanDefinition.getBeanClassName();
				try {
					log.info("====>注册HSF-API-Bean:{}", beanClassName);
					GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
					genericBeanDefinition.setBeanClass(HsfProxyFactoryBean.class);
					String[] stringArray = StringUtils.delimitedListToStringArray(beanClassName, ".");
					String beanName = StringUtils.uncapitalize(stringArray[stringArray.length - 1]);
					genericBeanDefinition.getPropertyValues().add("serviceInterface", ClassUtils.forName(beanClassName, classLoader));
					genericBeanDefinition.getPropertyValues().add("serviceUrl", hsfAttributes.get("url"));
					genericBeanDefinition.getPropertyValues().add("version", hsfAttributes.get("version"));
					genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
					genericBeanDefinition.setPrimary(true);
					registry.registerBeanDefinition(beanName, genericBeanDefinition);
				} catch (ClassNotFoundException e) {
					log.error("====>注册Bean:[{}]异常", beanClassName, e);
				}
			}
		}

		//注册单个Bean 配置 模式的
		for (Map.Entry<Class<?>, Map<String, Object>> mapEntry : hsfApiBeanSet.entrySet()) {
			Class<?> key = mapEntry.getKey();
			Map<String, Object> value = mapEntry.getValue();
			log.info("====>注册HSF-API-Bean:{}", key);
			try {
				GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
				genericBeanDefinition.setBeanClass(HsfProxyFactoryBean.class);
				String name = StringUtils.uncapitalize(key.getSimpleName());
				genericBeanDefinition.getPropertyValues().add("serviceInterface", key);
				genericBeanDefinition.getPropertyValues().add("serviceUrl", value.get("url"));
				genericBeanDefinition.getPropertyValues().add("version", value.get("version"));
				genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
				registry.registerBeanDefinition(name, genericBeanDefinition);
			} catch (BeansException e) {
				log.error("====>注册HSF-API-Bean:{},错误，当前Bean已存在", key);
			}
		}

		hsfClientBeanSet.forEach((k, v) -> {
			String beanClassName = k.getBeanClassName();
			try {
				log.info("====>注册HSF-Client-Bean:{}", beanClassName);
				String[] stringArray = StringUtils.delimitedListToStringArray(beanClassName, ".");
				String beanName = StringUtils.uncapitalize(stringArray[stringArray.length - 1]);
				GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
				genericBeanDefinition.setBeanClass(HsfProxyFactoryBean.class);
				genericBeanDefinition.getPropertyValues().add("serviceInterface", ClassUtils.forName(beanClassName, classLoader));
				genericBeanDefinition.getPropertyValues().add("hsfServiceInterfaceName", v.get("className"));
				genericBeanDefinition.getPropertyValues().add("serviceUrl", v.get("url"));
				genericBeanDefinition.getPropertyValues().add("version", v.get("version"));
				genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
				registry.registerBeanDefinition(beanName, genericBeanDefinition);
			} catch (ClassNotFoundException e) {
				log.error("====>注册Bean:[{}]异常", beanClassName, e);
			}
		});
	}

	private void groupHsfClient(Set<BeanDefinition> candidateComponents, Map<Class<?>, Map<String, Object>> hsfApiBeanSet, Map<BeanDefinition, Map<String, Object>> hsfClientBeanSet, Map<String, Map<String, Object>> hsfClientPackageAttributes, Set<String> basePackages) {
		for (BeanDefinition candidateComponent : candidateComponents) {
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				// verify annotated class is an interface
				AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(), "@HsfClient can only be specified on an interface");

				Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(HsfClient.class.getCanonicalName());
				if (attributes == null) {
					return;
				}

				Class<?>[] clazz = (Class<?>[]) attributes.get("value");
				String clazzName = (String) attributes.get("className");
				String[] packages = (String[]) attributes.get("basePackages");
				//校验 ： value 和  basePackages 不能全部设置为空
				Assert.isTrue(isNotEmpty(clazz) || isNotEmpty(packages) || StringUtils.hasText(clazzName), "value and className and basePackages can not both null");
				//校验 url 和 version
				String url = getUrl(attributes);
				Assert.isTrue(StringUtils.hasText(url), "@HsfClient url can not be null");

				attributes.put("url", url);

				String version = getVersion(attributes);
				Assert.isTrue(StringUtils.hasText(version), "@HsfClient version can not be null");

				attributes.put("version", version);

				//指明clazz的话，就加载clazz->bean
				if (isNotEmpty(clazz)) {
					for (Class<?> aClass : clazz) {
						hsfApiBeanSet.put(aClass, attributes);
						//如果是class的形式,把本身也变成bean
						attributes.put("className", aClass.getName());
						hsfClientBeanSet.put(candidateComponent, attributes);
					}
				}

				//如果是不依赖jar的配置的话,只用配置hsfClientBean
				if (StringUtils.hasText(clazzName)) {
					hsfClientBeanSet.put(candidateComponent, attributes);
				}

				//指明扫描包的话，扫描指定包
				if (isNotEmpty(packages)) {
					for (String pkg : packages) {
						if (StringUtils.hasText(pkg)) {
							basePackages.add(pkg);
							if (hsfClientPackageAttributes.keySet().contains(pkg)) {
								System.err.println("[HsfClientsRegistrar waring], same pkg config two hsfClient, pkg:" + pkg);
								continue;
							}
							hsfClientPackageAttributes.put(pkg, attributes);
						}
					}
				}
			}
		}
	}

	/**
	 * 获取url
	 *
	 * @param attributes
	 * @return
	 */
	private String getUrl(Map<String, Object> attributes) {
		String url = resolve((String) attributes.get("url"));
		if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
			if (!url.contains("://")) {
				url = "http://" + url;
			}
			try {
				new URL(url);
			} catch (MalformedURLException e) {
				throw new IllegalArgumentException(url + " is malformed", e);
			}
		}
		return url;
	}

	/**
	 * 获取版本
	 *
	 * @param attributes
	 * @return
	 */
	private String getVersion(Map<String, Object> attributes) {
		return resolve((String) attributes.get("version"));
	}

	/**
	 * 获取配置的属性
	 *
	 * @param value 配置key
	 * @return 配置value
	 */
	private String resolve(String value) {
		if (StringUtils.hasText(value)) {
			return this.environment.resolvePlaceholders(value);
		}
		return value;
	}

	/**
	 * 第一层的扫描器，扫描出标有 hsfClient 的类
	 *
	 * @return
	 */
	private ClassPathScanningCandidateComponentProvider getScanner() {
		return new ClassPathScanningCandidateComponentProvider(false, this.environment) {

			@Override
			protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
				return HsfClientsRegistrar.this.isCandidateComponent(beanDefinition);
			}
		};
	}

	/**
	 * 普通扫描器,hsfClient 的扫描器
	 *
	 * @return 扫描器
	 */
	private ClassPathScanningCandidateComponentProvider getHsfClientScanner() {
		return new ClassPathScanningCandidateComponentProvider(false, this.environment) {

			@Override
			protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
				return true;
			}

			@Override
			protected boolean isCandidateComponent(MetadataReader metadataReader) {
				return true;
			}
		};
	}

	private boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		if (beanDefinition.getMetadata().isIndependent()) {
			if (beanDefinition.getMetadata().isInterface()
					&& beanDefinition.getMetadata().getInterfaceNames().length == 1
					&& Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
				try {
					Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), HsfClientsRegistrar.this.classLoader);
					return !target.isAnnotation();
				} catch (Exception ex) {
					log.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);
				}
			}
			return true;
		}
		return false;
	}

	/**
	 * 是否为空数组
	 *
	 * @param array 数组
	 * @return true - 空数组
	 */
	private boolean isNotEmpty(final Object array) {
		return array != null && Array.getLength(array) != 0;
	}
}
